using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace RaidLog.Engine
{
    public class WowLogFile : StreamReader
    {
        public WowLogFile(string path) : base(path)
        {
        }

        public DateTime CurrentTime()
        {
            lock (this)
            {
                long pos = this.BaseStream.Position;
                try
                {
                    DateTime? d;
                    
                    // If we just read a line, we're at the beginning of the next.
                    d = GetTimeFromLineText(this.ReadLine());
                    if (d.HasValue) return d.Value;

                    // Maybe we're in the middle of a line. Try again.
                    d = GetTimeFromLineText(this.ReadLine());
                    if (d.HasValue) return d.Value;
                }
                finally { this.BaseStream.Seek(pos, SeekOrigin.Begin); this.DiscardBufferedData(); }
            }
            return DateTime.MinValue;
        }

        public DateTime MinTime()
        {
            lock (this)
            {
                long pos = this.BaseStream.Position;
                try
                {
                    this.BaseStream.Position = 0;
                    this.DiscardBufferedData();
                    return CurrentTime();
                }
                finally { this.BaseStream.Seek(pos, SeekOrigin.Begin); this.DiscardBufferedData(); }
            }
        }

        public DateTime MaxTime()
        {
            lock (this)
            {
                long pos = this.BaseStream.Position;
                try
                {
                    this.BaseStream.Seek(0, SeekOrigin.End);
                    this.DiscardBufferedData();
                    while (DateTime.MinValue == CurrentTime())
                    {
                        this.BaseStream.Seek(-5, SeekOrigin.Current);
                        this.DiscardBufferedData();
                    }
                    return CurrentTime();
                }
                finally { this.BaseStream.Seek(pos, SeekOrigin.Begin); this.DiscardBufferedData(); }
            }
        }

        public void SeekTime(DateTime time)
        {
            lock (this)
            {
                long delta = this.BaseStream.Length / 4L;
                this.BaseStream.Seek(this.BaseStream.Length / 2L, SeekOrigin.Begin);
                this.DiscardBufferedData();
                while (delta > 1)
                {
                    this.DiscardBufferedData();
                    this.BaseStream.Seek(((CurrentTime() > time) ? -1 : 1) * delta, SeekOrigin.Current);
                    delta /= 2;
                }
            }
        }

        public static DateTime? GetTimeFromLineText(string thisLine)
        {
            if (string.IsNullOrEmpty(thisLine)) return null;// throw new ApplicationException();
            Match m = Regex.Match(thisLine, @"^(\d\d?)/(\d\d?) (\d\d?):(\d\d):(\d\d)\.(\d\d\d) ");
            if (!m.Success) return null;// throw new InvalidOperationException("Time data not found");
            return new DateTime(2007,
                Int32.Parse(m.Groups[1].Value),
                Int32.Parse(m.Groups[2].Value),
                Int32.Parse(m.Groups[3].Value),
                Int32.Parse(m.Groups[4].Value),
                Int32.Parse(m.Groups[5].Value),
                Int32.Parse(m.Groups[6].Value));
        }

        public IEnumerable<string> GetLines(DateTime dateTimeStart, DateTime dateTimeEnd)
        {
            Trace.WriteLine(String.Format("Starting at: {0}", dateTimeStart));
            SeekTime(dateTimeStart);
            long startPos = BaseStream.Position;
            Trace.WriteLine(String.Format("Ending at: {0}", dateTimeEnd));
            SeekTime(dateTimeEnd);
            long endPos = BaseStream.Position;
            SeekTime(dateTimeStart);

            string thisLine;
            while ((BaseStream.Position < endPos) && null != (thisLine = ReadLine()))
            {
                yield return thisLine;
            }
        }

        public IEnumerable<string[]> GetLineSets(DateTime dateTimeStart, DateTime dateTimeEnd, int lineSetSize)
        {
            List<string> lineBuffer = new List<string>(lineSetSize);
            foreach(string thisLine in GetLines(dateTimeStart, dateTimeEnd))
            {
                lineBuffer.Add(thisLine);
                if (lineBuffer.Count >= lineSetSize)
                {
                    yield return lineBuffer.ToArray();
                    lineBuffer.Clear();
                }
            }
        }
    }
}

